package com.qiongqi.datadir.controller;

import com.qiongqi.datadir.common.AjaxResult;
import com.qiongqi.datadir.common.BaseController;
import com.qiongqi.datadir.common.page.TableDataInfo;
import com.qiongqi.datadir.config.DataDirConfig;
import com.qiongqi.datadir.domain.BucketData;
import com.qiongqi.datadir.domain.BucketQuota;
import com.qiongqi.datadir.domain.DataProvContract;
import com.qiongqi.datadir.domain.Resource;
import com.qiongqi.datadir.helper.VaultHelper;
import com.qiongqi.datadir.service.IBucketQuotaService;
import com.qiongqi.datadir.service.IDataProvContractService;
import com.qiongqi.datadir.service.IMinioService;
import com.qiongqi.datadir.service.IResourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import paillierp.Paillier;

import java.io.*;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.qiongqi.datadir.utils.file.CsvEncUtils.heEncFileAsStream;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.apache.commons.collections4.CollectionUtils.isEmpty;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.collections4.IterableUtils.contains;
import static org.apache.commons.collections4.IterableUtils.size;
import static org.apache.commons.collections4.MapUtils.getString;
import static org.apache.commons.lang3.StringUtils.*;

@Slf4j
@Controller
@RequestMapping("/ts")
public class TrustedSpaceController extends BaseController {

    @Autowired
    private DataDirConfig dataDirConfig;

    @Autowired
    private IMinioService minioService;

    @Autowired
    private IBucketQuotaService bucketQuotaService;

    @Autowired
    private IResourceService resourceService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private IDataProvContractService dataProvContractService;

    @Autowired
    private VaultHelper vaultHelper;


    /**
     * 查询可信空间列表
     */
    @GetMapping
    public String index(Model model) {
        model.addAttribute("acconName", dataDirConfig.getName());
        return "ts";
    }

    /**
     * 查询可信空间列表
     */
    @GetMapping("/add")
    public String add() {
        return "tsadd";
    }

    /**
     * 创建可信空间
     */
    @PostMapping("/create")
    @ResponseBody
    public AjaxResult create(@RequestBody BucketQuota bucketQuota) {
        log.info("bucketQuota:{}", bucketQuota);
        if (isNull(bucketQuota) || isBlank(bucketQuota.getName())) {
            return error("可信数据空间参数非法!");
        }
        if (bucketQuotaService.checkBucketNameIfExisted(bucketQuota.getName())) {
            return error("同名可信数据空间已经存在!");
        }
        List<String> participants = bucketQuota.getParticipants();
        if (isEmpty(participants)) {
            return error("可信数据空间没有设置参与方!");
        }
        if (!contains(participants, dataDirConfig.getName())) {
            participants.add(dataDirConfig.getName());
        }
        boolean ret = nonNull(bucketQuota) ? minioService.createBucketWithQuota(bucketQuota) : false;
        return ret ? success() : error();
    }

    /**
     * 查询资源记录列表
     */
    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(BucketQuota bucketQuota) {
        startPage();
        List<BucketQuota> list = bucketQuotaService.selectBucketQuotaList(bucketQuota);
        return getDataTable(list);
    }

    /**
     * 添加数据资源
     * 调用示例: POST /addRes?bucketQuotaId=quota-123
     * Content-Type: application/json
     */
    @PostMapping("/addRes")
    @ResponseBody
    public AjaxResult addRes(
            @RequestBody Map<String, String> reqBody) throws Exception {
        int ret = -1;
        String bucketName = getString(reqBody, "bucketName");
        String resId = getString(reqBody, "resId");
        if (isBlank(bucketName)) {
            return error("非法的可信空间");
        }
        if (isBlank(resId)) {
            return error("非法的资源参数");
        }
        Resource resource = resourceService.selectResourceById(resId);
        if (isBlank(resource.getRemark())) {
            return error("非法的资源");
        }
        BucketData bucketData = new BucketData(resource.getCreateBy(), resource.getId(), resource.getName());
        DataProvContract dpc = dataProvContractService.selectDataProvContractByResId(resource.getId());

        if(equalsIgnoreCase(resource.getType(), "DB")) {
            // 设置请求头，明确接受 text/csv 类型
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Collections.singletonList(MediaType.parseMediaType("text/csv")));
            HttpEntity<String> entity = new HttpEntity<>(headers);
            File outputFile = restTemplate.execute(
                    resource.getRemark(), // 文件下载URL
                    HttpMethod.GET,
                    null,
                    new ResponseExtractor<File>() {
                        @Override
                        public File extractData(ClientHttpResponse response) throws IOException {
                            // 创建临时文件
                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
                                    .withZone(ZoneId.systemDefault());
                            String timestampStr = formatter.format(Instant.now());
                            File tempFile = File.createTempFile("download-" + timestampStr, ".csv");
                            try (InputStream inputStream = response.getBody();
                                 FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                                // 流式复制，避免内存溢出
                                StreamUtils.copy(inputStream, outputStream);
                            }
                            return tempFile;
                        }
                    }
            );
            log.info("outputFile:{}", outputFile);
            if (nonNull(outputFile)) {
                List<String> encFields = nonNull(dpc) ? dpc.getDataProvEncFields() : null;
                Paillier paillier = vaultHelper.getPublicKeyShare(bucketName);
                outputFile = heEncFileAsStream(outputFile, encFields, paillier);
                ret = isNotBlank(bucketName) && nonNull(bucketData) ?
                        minioService.putObject(bucketName, bucketData, outputFile) : -1;
            }
        }else if (equalsIgnoreCase(resource.getType(), "FILE")) {
            // 设置请求头，明确接受 PDF 类型
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_PDF)); // 或使用 MediaType.parseMediaType("application/pdf")
            HttpEntity<String> entity = new HttpEntity<>(headers);
            File outputFile = restTemplate.execute(
                    resource.getRemark(), // 文件下载URL
                    HttpMethod.GET,
                    null,
                    new ResponseExtractor<File>() {
                        @Override
                        public File extractData(ClientHttpResponse response) throws IOException {
                            // 创建临时文件，后缀改为 .pdf
                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
                                    .withZone(ZoneId.systemDefault());
                            String timestampStr = formatter.format(Instant.now());
                            // 创建临时文件，指定后缀为 .pdf
                            File tempFile = File.createTempFile("download-" + timestampStr, ".pdf");
                            // 建议记录临时文件路径，便于调试和后续处理
                            log.info("PDF临时文件创建在: {}", tempFile.getAbsolutePath());
                            try (InputStream inputStream = response.getBody();
                                 FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                                // 流式复制，避免内存溢出
                                StreamUtils.copy(inputStream, outputStream);
                                log.info("PDF文件流复制完成。");
                            } catch (IOException e) {
                                log.error("下载或写入PDF文件过程中发生IO异常", e);
                                // 如果出现异常，删除可能不完整的临时文件
                                if (tempFile.exists() && !tempFile.delete()) {
                                    log.warn("无法删除临时文件: {}", tempFile.getAbsolutePath());
                                }
                                throw e; // 重新抛出异常，让上层处理
                            }
                            return tempFile;
                        }
                    }
            );
            log.info("PDF文件下载完成，输出文件: {}", outputFile);
            // 后续处理，例如加密、上传到MinIO等，与你现有的CSV逻辑类似
            if (nonNull(outputFile)) {
                // 上传到MinIO
                ret = isNotBlank(bucketName) && nonNull(bucketData) ?
                        minioService.putObject(bucketName, bucketData, outputFile) : -1;
                // 根据你的业务需求，可能在这里或最后删除本地临时PDF文件
                // if (outputFile.delete()) { log.debug("临时PDF文件已删除"); } else { log.warn("临时PDF文件删除失败"); }
            }
        }
        return ret > 0 ? success() : error();
    }

    /**
     * 移除数据资源
     * 调用示例: POST /removeRes?bucketQuotaId=quota-123
     * Content-Type: application/json
     */
    @PostMapping("/removeRes")
    @ResponseBody
    public AjaxResult removeRes(
            @RequestParam String bucketName,
            @RequestBody BucketData bucketData) {
//        int ret = isNotBlank(bucketName) && nonNull(bucketData) ? minioService.removeDataFromBucket(bucketQuotaId, bucketData) : -1;
        int ret = -1;
        return ret > 0 ? success() : error();
    }

    @Async("threadPoolTaskExecutor")
    @Scheduled(initialDelay = 10000, fixedRate = 43_200_000)
    @GetMapping("/reclaim")
    public String scheduledReclaimTrustedSpace() {
        List<BucketQuota> bqs = minioService.getAllExpiredBuckets();
        int ret = 0;
        if (isNotEmpty(bqs)) {
            for (BucketQuota bq : bqs) {
                if (minioService.removeBucketByName(bq.getName())) {
                    ret++;
                }
            }
        }
        log.info("{} buckets needs to be remove, removed {} buckets", size(bqs), ret);
        return "" + ret;
    }
}
